#include <cstdarg>
#include <cassert>
/* parallel comunication */
//#ifdef PARALELL
#ifdef PARALLEL
#include <mpi.h>
#include "pch_intf.h"
#endif
#include "Log.h"
#include "WindowFV.h"
// namespaces
namespace FemViewer {
namespace IO        {

Log* Log::_selfp = NULL;
bool Log::_init = false;
const int MAX_LOG_STRING = 1024;
const char* LogFileName  = "internal_log.txt";

bool Log::Init(int mode,FILE* fp,mfvWindow* dlgp)
{
	if(!_selfp) {
		_selfp = new Log(mode,fp,dlgp);
		atexit(Log::Close);
	}
	_init = (_selfp!=0);
	return _init;
}

void Log::Close()
{
	if(_selfp) delete _selfp;
	_selfp = 0;
}

Log& Log::GetInstance()
{
	if(!_selfp) _selfp = new Log;
	_init = (_selfp != NULL);
	return *_selfp;
}

Log::Log(int mode,FILE* fp,mfvWindow* dlgp)
: _mode(mode), _logfilep(fp), _logdlgp(NULL)
{
	if (_logfilep) StartLogToFile();
}

Log::~Log()
{
	if (_logdlgp) delete _logdlgp;
	if (_logfilep) EndLogToFile();
}

void Log::msg(const char* str)
{
	bool lflag = (_logfilep == stdout) ? true : false;
	if (lflag) {
		fprintf(_logfilep,str); fflush(_logfilep);
		return;
	}
	if(_logfilep && (_mode & LOGFILE)) {
		fprintf(_logfilep,str);
	} else if(_mode & LOGFILE) {
		if(_slogstrm.is_open()) _slogstrm << "=> " << str << std::endl << std::flush;
	}
	if(_mode & LOGCONSOLE) {
		fprintf(stdout,str);
		fflush(stdout);
	}
	if(_mode & LOGDIALOG) {
		assert(!"Not implemented!");
	}
}

void Log::SetMode(const unsigned int mode)
{
	unsigned lmode(0);
	bool flag = false;
	if(mode & LOGCONSOLE) { lmode |= LOGCONSOLE; flag = true; }
	if(mode & LOGFILE)    { lmode |= LOGFILE; flag = true; }
	if(mode & LOGDIALOG)  { lmode |= LOGDIALOG; flag = true; }
	if(flag) _mode = lmode;
}

void Log::StartLogToFile()
{
	_slogstrm.open(LogFileName,std::ios::in);
	if(_slogstrm.fail()) return;

	_slogstrm << "______Strat Log_______\n\n"<< std::flush;
}

void Log::EndLogToFile()
{
	_slogstrm << "______End Log________\n" << std::flush;
	_slogstrm.close();
}

#ifdef PARALLEL
int Log::sendlog(const char *str) {
	return 1;
}

int Log::updatelog(const char *str) {
	return 1;
}
#endif

void log(const char* msg,...)
{
    char buffer[MAX_LOG_STRING];

    // do the formating
    va_list valist;
    va_start(valist, msg);
#ifdef _WIN32
	_vsnprintf(buffer,MAX_LOG_STRING,msg,valist);
#else
    vsprintf(buffer, msg, valist);
#endif
    va_end(valist);

    Log::GetInstance().msg(buffer);
}

#ifdef PARALLEL
	void printParallel(const char* msg, ...)
	{
		// Sanity check
		//assert("pre: MPI controller is NULL!" && (comm != NULL) );
		//assert("pre: format argument is NULL!" && (format != NULL) )
		if( ::pcr_my_proc_id() == ::pcr_print_master())
		{
		    log(msg);
		    //printf("\nHello\n");
		    //fflush(stdout);
		}

		 ::pcr_barrier();
	}

	void printSynchronizedParallel(const char* msg, ...)
	{
		MPI_Request *request;
		// Sanity checks
		int procId  = ::pcr_my_proc_id();
		int numProc = ::pcr_nr_proc();
		char buffer[MAX_LOG_STRING];
		sprintf(buffer,"[%d]: %s",procId,msg);
		int* nullmsg = NULL;

		if (procId == ::pcr_print_master()) {
			// print message
		    log(buffer);

		    // signal next process (if any) to print
		    if (numProc > 1)
		    {
		    	MPI_Isend(nullmsg, 0, MPI_INT, 1, 0, MPI_COMM_WORLD,request);
		    }
		}
		else if (procId == numProc) {
		    // Block until previous process completes
		    ::pcr_receive_int(procId-1,0,0,nullmsg);

		    // print message
		    log(buffer);
		} // END last rank
		else {
		    // Block until previous process completes
		    ::pcr_receive_int(procId-1,0,0,nullmsg);

		    // print message
		    log(buffer);

		    // signal next process to print
		    MPI_Isend(nullmsg, 0, MPI_INT, procId, 0, MPI_COMM_WORLD,request);
		}

		::pcr_barrier();
	}
#endif



}// end namespace IO
}// end namespace FemViewer
